:- module(bfs).
:- export bfs_instance / 1.
:- export unify_bfs / 2.
:- comment(summary, "Best-first search library").
:- comment(author, "Andrew Eremin").
:- comment(copyright, "Imperial College London and Parc Technologies").
:- comment(date, "$Date: 2004/12/15 16:39:04 $").
:- comment(status, prototype).
:- comment(desc, html("<P>\n   This library lets you use customisable branch-and-bound search. It\n   is primarily intended for use with the colgen library in implementing\n   branch-and-price algorithms, but can be used with arbitrary\n   user-defined node solution, separation and selection goals.\n   When application specific methods are not required for node\n   separation and/or selection the use of built-in depth-first\n   and best-first node selection, and branching on most fractional\n   variable or generalised upper bound constraint may be specified.\n   When the relaxed problem to be solved at a node involves an eplex\n   instance branching may additionally be specified by built-in\n   objective coefficient, estimate- or lower-bounding based methods,\n   and best-estimate node selection may be used.\n</P>\n")).
:- comment(bfs_branch / 1, [template : "BfsInstance:bfs_branch(+Branch)", args : ["Branch" : "Prolog term"], summary : "Post a branching constraint to the bfs instance BfsInstance.", see_also : [solver_setup / 2, solver_setup / 3, get / 2], desc : html("<P>\n   Post a new branching constraint <TT>Branch</TT> to the bfs instance\n   BfsInstance. The constraint will be used to create a new child node\n   of the current open node in the search tree for BfsInstance.\n   <TT>Branch</TT> may be any prolog term, but clearly should be an\n   appropriate constraint for the node relaxation solver associated with\n   BfsInstance. \n</P>")]).
:- comment(bfs_instance / 1, [amode : bfs_instance(++), args : ["BfsInstance" : "Bfs instance name (atom)"], summary : "Initialises the bfs instance BfsInstance.", desc : html("  <P>\n  Initialises the bfs instance BfsInstance. A bfs instance is an\n  instance of the best first search solver, with which node\n  relaxation and separation solvers can be associated and used to\n  optimise the problem constraints posted to the relaxed node solver\n  with respect to its objective using a specified node ordering scheme.\n  In particular best-first and best-estimate search schemes are\n  supported and application-specific schemes may be easily defined by\n  the user.\n  </P><P>\n  If BfsInstance is not an already existing bfs instance, a new bfs\n  instance will be created and initialised. If it is an existing bfs\n  instance, and it is not currently being used (having no associated\n  solvers), it is effectively reinitialised. Otherwise, the predicate\n  aborts with an error. Note that a bfs instance is a module, and each\n  bfs instance can be associated with at most one relaxation and one\n  separation solver at any time and vice versa.\n  </P>\n  "), see_also : [integers / 1, bfs_branch / 1, node_info / 5, solver_setup / 2, solver_setup / 3, solve / 1, get / 2, var_get / 3, bfs : statistics / 0]]).
:- comment(get / 2, [template : "BfsInstance:get(++What, -Value)", args : ["What" : "Specification for information wanted (atom)", "Value" : "Returned value of What"], summary : "Obtain information for the problem associated with BfsInstance.", exceptions : [6 : "What is not a valid value."], desc : html("<P>\n   Retrieve information about the problem associated with the bfs\n   instance BfsInstance. What can take one of the following values:\n\n<DL>\n    <DT><TT>frac_vars</TT>\n    <DD>Returns the list of variables declared as integer for\n    BfsInstance which have fractional solution values in the current node\n    of the search tree.\n<P>\n\n    <DT><TT>branches</TT>\n    <DD>Returns the branching decisions taken from the root to the\n    current node of the search tree.\n</DL>")]).
:- comment(integers / 1, [template : "BfsInstance:integers(+Ints)", args : ["Ints" : "A variable or list of variables"], summary : "Declare Ints as integers in BfsInstance.", desc : html("<P>\n   The variable or list of variables Ints will be considered integers\n   when solving the problem associated with BfsInstance. Note that\n   this does not mean that they will be treated as integers by the\n   specified node relaxation solver, rather that they will be considered\n   as candidates for branching decisions in nodes where their solution\n   value is not integral (to within a tolerance parameter set in the\n   options list of BfsInstance:solver_setup/3). The predefined\n   node separation schemes will branch on these variables. User-defined\n   node separation predicates may access the fractional variables\n   at the current node via a call to BfsInstance:get/2"), see_also : [solver_setup / 3, get / 2]]).
:- comment(node_cost / 1, [template : "BfsInstance:node_cost(+Val)", args : ["Val" : "Solution cost for problem (number)"], summary : "Set solution cost for the problem at a node.", desc : html("<P>\n   Set the solution cost for the problem at the current open node of\n   the search tree associated with the bfs instance BfsInstance.\n</P>")]).
:- comment(node_info / 5, [template : "BfsInstance:node_info(+Var, ?Lo, ?Hi, ?Val, ?RC)", args : ["Var" : "A solver problem variable for solver the associated with BfsInstance", "Lo" : "Lower bound for Var (number)", "Hi" : "Upper bound for Var (number)", "Val" : "Solution value for Var (number)", "RC" : "Reduced cost for Var (number)"], summary : "Get or set node bounds and solution information for an individual solver problem variable Var.", desc : html("<P>\n   Retrieve or update bounds and solution information for an\n   individual solver problem variable Var for the current open node of\n   the search tree associated with the bfs instance BfsInstance. If Var\n   was not already a problem variable for BfsInstance it will now be\n   considered one. If reduced costs are available in problems\n   involving eplex instances supplying these to the bfs solver can lead\n   to improved pruning.\n</P>")]).
:- comment(solve / 1, [template : "BfsInstance:solve(-Cost)", args : ["Cost" : "The optimal solution cost of the problem associated with BfsInstance"], summary : "Optimise the problem associated with BfsInstance.", fail_if : "No solution exists satisfying the global feasibility conditions.", see_also : [solver_setup / 2, solver_setup / 3, get / 2, var_get / 3, bfs : statistics / 0], desc : html("<P>\n   A solver setup with solver_setup/2 or solver_setup/3 is triggered\n   using this predicate.\n</P><P>\n   The node relaxation and separation solvers are applied to the next\n   selected node of the problem represented by Handle repeatedly until\n   no more open nodes remain. The criteria for node selection order\n   depends on the options given to solver_setup/2,3.  solve/1 fails if\n   there is no solution or succeeds if an optimal solution is found,\n   returning the solution's cost in Cost.  After a success, various\n   solution information and statistics can be retrieved using get/2,\n   var_get/3 and statistics/0.\n</P>")]).
:- comment(solver_setup / 2, [template : "BfsInstance:solver_setup(+OptSense, +Solver)", args : ["OptSense" : "Optimisation direction: min or max", "Solver" : "Node relaxation solver"], summary : "Setup a bfs solver tree for bfs instance BfsInstance.", see_also : [integers / 1, bfs_branch / 1, node_info / 5, solver_setup / 3, solve / 1, get / 2, var_get / 3, bfs : statistics / 0], desc : html("<P>\n  Setup a new solver tree for the bfs instance BfsInstance. The tree\n  will be associated with BfsInstance; BfsInstance must not already\n  have a solver tree associated with it. Once the solver tree is setup,\n  it can be optimised via solve/1.\n</P><P>\n  This is a simplified version of solver_setup/3, it is equivalent to\n  calling solver_setup/3 with the following default options:\n<PRE>\n       solver_setup(OptSense, Solver, [])\n</PRE>\n")]).
:- comment(solver_setup / 3, [template : "BfsInstance:solver_setup(+OptSense, +Solver, ++ListOfOptions)", args : ["OptSense" : "Optimisation direction: min or max", "Solver" : "Node relaxation solver", "ListOfOptions" : "List of solver options"], summary : "Setup a bfs solver tree for bfs instance BfsInstance.", see_also : [integers / 1, bfs_branch / 1, node_info / 5, solver_setup / 2, solve / 1, get / 2, var_get / 3, bfs : statistics / 0], desc : html("<P>\n  Setup a new solver tree for the bfs instance BfsInstance. The tree\n  will be associated with BfsInstance; BfsInstance must not already\n  have a solver tree associated with it. Once the solver tree is setup,\n  it can be optimised via solve/1. This predicate allow various\n  options to be specified when setting up the  solver state via\n  <TT>ListOfOptions</TT>.\n</P><P>\n  <TT>OptSense</TT> is the optimisation direction. <B>Note</B> that this\n  is assumed to be the same as the sense of optimisation used in\n  <TT>Solver</TT>. It is the user's responsibility to ensure \n  that this is in fact the case. <TT>OptSense</TT> is used internally\n  for bound updates and pruning, and for node ordering with the built-in\n  best-first and best-estimate node selection methods.\n</P><P>\n  <TT>Solver</TT> is the node relaxed problem solver. It is either a\n  user-defined predicate or an eplex instance or handle, for which a\n  built-in node relaxation solver is available.\n\n</P><P>\nListOfOptions are:\n\n<DL>\n\n<P>\n<DT><STRONG><TT>separation(+Separation)</TT></STRONG>\n    <DD>Use the specified method to separate the current node.\n    <TT>Separation</TT> is either a user-defined predicate, or one of\n    the atoms <TT>fracvar, enhanced, strong, deg_est</TT>\n    corresponding to the built-in separation methods. Note that the\n    methods <TT>enhanced, strong, deg_est</TT> are only available when\n    the node relaxation solver involves an eplex instance.\n    <TT>Separation</TT> defaults to fracvar.\n\n<P>\n<DT><STRONG><TT>node_select(+Select)</TT></STRONG>\n    <DD>Use the specified method (<TT>depth_first, best_first,\n    best_estimate</TT>) to select the next open node for solution and\n    separation. <TT>Select</TT> defaults to best_first.\n\n<P>\n<DT><STRONG><TT>alpha(?AlphaMin, ?AlphaMax)</TT></STRONG>\n    <DD>When using estimate- or lower-bounding based dichotomic node\n    separation methods the overall value assigned to branching on a\n    particular variable or constraint is calculated as a weighted sum\n    of the estimates obtained for the two branches it would produce.\n    AlphaMin is the weighting given to the minimum of the two estimates\n    and AlphaMax to the maximum. <TT>AlphaMin</TT> and <TT>AlphaMax</TT>\n    are numbers and default to 2 and 1 respectively.\n\n<P>\n<DT><STRONG><TT>beta(?BetaConst, ?BetaPC, ?BetaLB)</TT></STRONG>\n    <DD>When using estimate- or lower-bounding based node separation\n    methods with a problem involving an eplex instance the estimate\n    assigned to each branch produced by branching on a particular\n    variable or constraint is calculated as a weighted sum  of the\n    pseudo-cost estimate and the lower bound. BetaPC is the weighting\n    given to the pseudo-cost estimate and BetaLB to the lower bound.\n    BetaConst is a constant offset only used when linear regression is\n    employed to update these values during search. <TT>BetaConst</TT>,\n    <TT>BetaPC</TT>, <TT>BetaLB</TT> are numbers and default to 0, 1,\n    1 respectively.\n\n<P>\n<DT><STRONG><TT>pseudo_cost(?PCInit, ?PCUpdate, ?PCRatio)</TT></STRONG>\n    <DD>\n    <P>When using estimate-based dichotomic node separation methods\n    with a problem involving an eplex instance up and down pseudo-costs\n    are assigned to each variable and generalised upper bound constraint \n    branch-point representing the estimated degradation in objective\n    cost per unit change in variable or constraint value incurred on\n    that branch.\n    </P><P><TT>PCInit</TT> specifies the method used to initialise these\n    values when a variable or constraint branch-point is first considered\n    for branching:\n    <DL>\n    <DD><TT>average</TT> : the pseudocosts are initialised to the average\n    of the observed changes in cost of all up or down branches in the\n    search tree.\n    <DD><TT>cost</TT> : variable pseudocosts are initialised to the\n    objective cost coefficient of the variable, constraint branch-point\n    pseudocosts to the average of the cost coefficients of variables\n    involved.\n    <DD><TT>calculated</TT> : the pseudocosts are initialised to a\n    value calculated by performing a number of external solver iterations\n    equal to <TT>(PCRatio * #iterations in root node)/(2* #fractional\n    vars in root node)</TT>.\n    </DL>\n    The default is <TT>calculated</TT>.\n    </P><P><TT>PCUpdate</TT> is an atom specifying the method used to\n    update these values throughout the search tree once the variable\n    or constraint has been branched on:\n    <DL>\n    <DD><TT>average</TT> : the pseudocosts are updated to the average\n    of the observed changes in cost of all up or down branches in the\n    search tree for that variable or constraint. \n    <DD><TT>first</TT> : the pseudocosts are fixed to the observed\n    change in cost at the first up or down branch in the search tree for\n    that variable or constraint.\n    <DD><TT>last</TT> : the pseudocosts are fixed to the observed\n    change in cost at the last up or down branch in the search tree for\n    that variable or constraint.\n    </DL>\n    The default is <TT>average</TT>.\n    </P><P><TT>PCRatio</TT> is a float between 0 and 1 and is used in\n    calculating the number of external solver iterations to perform\n    when explicitly calculating initial pseudo-cost estimates; the\n    default value is 0.05. Setting small ratios will result in faster\n    node separation, but the initial estimates for variables and\n    constraints on which the branching decisions are taken will be less\n    accurate. Setting larger values will result in more work being performed\n    in node separation and better estimates for the branching\n    decisions. The optimum value will be problem specific, although in \n    general the overhead of performing a total number of iterations\n    more than a small ratio of the root node iterations will outweigh the\n    benefit obtained.\n\n<P>\n<DT><STRONG><TT>lower_bound(+Limit)</TT></STRONG>\n    <DD>When using lower-bounding based node separation methods with a\n    problem involving an eplex instance, specify how many external solver\n    iterations should be performed to calculate the lower bound.\n    <TT>Limit</TT> is an integer and defaults to 1. Setting small\n    values of iterations will result in faster node separation, but the\n    lower bounds on which the branching decisions are taken will be less\n    tight. Setting larger values will result in more work being performed\n    in node separation and tighter lower bounds for the branching\n    decisions. The optimum value will be problem specific, although in\n    general the overhead of performing more than a few iterations will\n    outweigh the benefit obtained.\n\n<P>\n<DT><STRONG><TT>int_tolerance(+IntTol)</TT></STRONG>\n    <DD>Specify how far from integrality an integer variable's node\n    solution can fall before it is considered for separation by the\n    built-in separation methods. <TT>IntTol</TT> is a float and\n    defaults to 0.00001.  \n<P>\n<DT><STRONG><TT>info_messages(+OnOff)</TT></STRONG>\n    <DD>Specify whether information messages should be output at\n    various points during solution. This option is most useful for\n    debugging purposes. OnOff is one of the atoms <TT>on</TT> or\n    <TT>off</TT>, the default is <TT>off</TT>.\n\n</DL>\n</P>")]).
:- comment(statistics / 0, [template : "BfsInstance:statistics", summary : "Display search tree statistics for bfs instance BfsInstance.", see_also : [solver_setup / 2, solver_setup / 3, solve / 1], desc : html("<P>\n  Display statistics for the search tree associated with the bfs\n  instance BfsInstance: total number of nodes created, total search\n  time, number of nodes solved, node solution time, number of nodes\n  separated, node separation time, total number of global feasible\n  solutions found, first and optimal global solution solution time.\n</P>\n")]).
:- comment(var_get / 3, [template : "BfsInstance:var_get(+Var, ++What, -Value)", args : ["Var" : "A solver problem variable for the solver associated with BfsInstance", "What" : "Specification for information wanted (atom)", "Value" : "Returned value of What"], summary : "Obtain information for an individual solver problem variable Var.", exceptions : [6 : "What is not a valid value."], desc : html("<P>\n   Retrieve information about solver results related to a particular\n   variable, for the bfs instance BfsInstance. Fails if Var is not a\n   problem variable for BfsInstance. What can take one of the following\n   values: \n\n<DL>\n    <DT><TT>optimal_val</TT>\n    <DD>Returns the floating-point solution for variable Var.\n<P>\n\n    <DT><TT>node_val</TT>\n    <DD>Returns the floating-point solution for variable Var for the\n    current node in the search tree of this instance.\n<P>\n\n    <DT><TT>type</TT>\n    <DD>Returns the type real or integer of Var in this instance.\n</DL>")]).
